package top.lcm889.juc.semaphore;

import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Coder-lcm
 * @date 2021/5/1
 *  Semaphore是用来保护一个或者多个共享资源的访问，Semaphore内部维护了一个计数器，其值为可以访问的共享资源的个数。一个线程要访问共享资源，先获得信号量，如果信号量的计数器值大于1，意味着有共享资源可以访问，则使其计数器值减去1，再访问共享资源。
 *
 * 如果计数器值为0,线程进入休眠。当某个线程使用完共享资源后，释放信号量，并将信号量内部的计数器加1，之前进入休眠的线程将被唤醒并再次试图获得信号量。
 *
 * 就好比一个厕所管理员，站在门口，只有厕所有空位，就开门允许与空侧数量等量的人进入厕所。多个人进入厕所后，相当于N个人来分配使用N个空位。为避免多个人来同时竞争同一个侧卫，在内部仍然使用锁来控制资源的同步访问。
 *
 */
public class SemaphoreUser implements Runnable{
    private ResourceManager resourceManager;
    private int userId;

    public SemaphoreUser(ResourceManager resourceManager, int userId){
        this.resourceManager = resourceManager;
        this.userId = userId;
    }

    @Override
    public void run() {
        System.out.println("userId: " + userId + " 准备使用资源");
        try {
            resourceManager.useResource(userId);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("userId: " + userId + " 使用资源完毕");
    }

    public static void main(String[] args) {
        ResourceManager resourceManager = new ResourceManager();
        Thread[] threads = new Thread[100];//模拟100上厕所的用户线程
        for (int i = 0; i < 100; i++) {
            threads[i] = new Thread(new SemaphoreUser(resourceManager, i));
        }
        for (int i = 0; i < 100; i++) {
            threads[i].start();//启动线程
        }
    }
}

/**
 * 公共厕所资源管理器：信号量机制
 */
class ResourceManager{
    private final Semaphore semaphore;
    private boolean[] resources;
    private final ReentrantLock lock;

    public ResourceManager(){
        // 共享资源数量设置为10,定义为公平模式的信号量
        this.semaphore = new Semaphore(10, true);
        this.lock = new ReentrantLock(true); //公平锁
        this.resources = new boolean[10];
        // 模拟10个公共厕所空位，true表示是空的
        for (int i = 0; i < 10; i++) {
            this.resources[i] = true;
        }
    }

    /**
     * 使用资源
     * @param userId
     */
    public void useResource(int userId) throws InterruptedException {
        semaphore.acquire();//获取信号量，计数器-1
        try {
            int id = getResource();//占到一个坑
            System.out.println("userId: " + userId + " 正在使用资源，资源id： " + id +  "\n");
            Thread.sleep(200);//相当于是在使用资源
            resources[id] = true;//使用完之后把资源归还
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            semaphore.release();//释放信号量，计数器+1
        }
    }

    /**
     * 申请资源（如果有空的厕所就返回厕所ID）
     * @return
     */
    public int getResource(){
        int id = -1;
        lock.lock();
        try {
            for (int i = 0; i < 10; i++) {
                if (resources[i]) {
                    // 找到空的厕所就占用它（设为false表示占用），返回厕所Id
                    resources[i] = false;
                    id = i;
                    break;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
        return id;
    }
}
